/**
 * Copyright 2010 The Regents of the University of California
 *
 *  All Rights Reserved
 */

#include <sys/types.h>
#include <inttypes.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <signal.h>

#include "new-switch-handler.h"
#include "dcswitch.h"

#include "openflow/openflow.h"
#include "flow.h"
#include "ofpbuf.h"
#include "learning-switch.h"
#include "vconn.h"
#include "xtoxll.h"
#include "fault-manager.h"
#include "packets.h"
#include "arp-handling.h"
#include "downward-entries.h"



#define THIS_MODULE VLM_build_fattree
#include "vlog.h"


void init_fattree_indices(void);
void insert_downward_flow_entries(void);
void print_connectivity(void);
bool isDownwordFacing(int switchIndex, int port);
int last_edge[K];
void print_pod(int pod);
int curr_pod = 0;


int new_lookup(unsigned long long int dp_id)
{
	int i;
	DCSwitch_node *node;

	for(i=0; i<MAX_SWITCHES; i++)
	{	
		node = DCSwitches[i];
		if(node == NULL) continue;
		if(node->datapath_id == dp_id)
			return i;
			
	}
	return -1;
}


int fattree_index(unsigned long long int dp_id)
{
	int i;
	DCSwitch_node *node;

	for(i=0; i<MAX_SWITCHES; i++)
	{	
		node = DCSwitches[i];
		if(node->datapath_id == dp_id)
			return node->fattree_index;
			
	}
	return -1;
}


void assign_core_indices(DCSwitch_node *node, int index)
{
	if(node->pod!=-1 && node->fattree_index!=-1)
		return;
	node->pod = K;
	node->fattree_index = index;
	if(index>MAX_SWITCHES) 
		VLOG_WARN("More core switches than expected something's wrong. Core fattree_index: %d", index);
	return;
}	
				

void assign_aggr_indices(DCSwitch_node* my_node)
{
	int i,j=0;
	int aggr_ct=1;
	DCSwitch_node* node = DCSwitches[my_node->index];
	DCSwitch_node* edge_node;

	if(my_node->pod!=-1 && my_node->fattree_index!=-1);
	else
	{
		node->pod = curr_pod;
		node->fattree_index = curr_pod*K + K/2 ;
		curr_pod+=1;
		VLOG_WARN("In pod: %d  Aggr switch: assigned ft %d index %d dpid %llx", node->pod, node->fattree_index, node->index, node->datapath_id);
	}
	
	for(i=0; i<K; i++)
	{
		if(node->ports[i][1] == 0 && node->neighbours[i]!=NULL)
		{
			edge_node = node->neighbours[i];
			if(edge_node->pod==-1 && edge_node->fattree_index==-1)
			{
				(node->neighbours[i])->pod = node->pod;
				(node->neighbours[i])->fattree_index = (node->pod)*K + last_edge[node->pod];
				last_edge[node->pod]++;

				for(j=0; j<K; j++)
				{
					if(edge_node->ports[j][1] == 1 && edge_node->neighbours[j]!=NULL)
					{
						if((edge_node->neighbours[j])->pod == -1 && (edge_node->neighbours[j])->fattree_index == -1)
						{
							(edge_node->neighbours[j])->pod = node->pod;
							(edge_node->neighbours[j])->fattree_index = node->pod*K + K/2 + aggr_ct;
//							VLOG_WARN("Switch %d %d %d Assigned fattree index: %d Known Index %d Random Index %d", edge_node->fattree_index, edge_node->sw_indexTmp, edge_node->index, (edge_node->neighbours[i])->fattree_index, (edge_node->neighbours[i])->sw_indexTmp, (edge_node->neighbours[i])->index);
							aggr_ct++;
				
						}
					}
				}
			}
		}
	}
}

void assign_edge_indices(DCSwitch_node* my_node, int *curr_pod)
{
	
}	


void dump_pod_nodes(DCSwitch_node* node)
{
	int i,j=0;
	DCSwitch_node* edge_node[K/2], *aggr_node[K/2];
	DCSwitch_node* edge; 	
	VLOG_WARN("Nodes in Pod: %d", node->pod);
		
	for(i=0; i<K; i++)
	{
		if(node->ports[i][1] == 0 && node->neighbours[i]!=NULL)
		{
			edge_node[j] = node->neighbours[i];
			VLOG_WARN("Pod: %d Edge Switches: %d(%d)(%d)\t",edge_node[j]->pod, edge_node[j]->fattree_index, edge_node[j]->sw_indexTmp, edge_node[j]->index);
			j++;
		}
	}
//What if all the neighbours for a switch are NULL: Need to check for that
	edge = edge_node[0];
	j=0;
	
	for(i=0; i<K; i++)
	{
		if(edge->ports[i][1] == 1 && edge->neighbours[i] != NULL)
		{
			aggr_node[j] = edge->neighbours[i];
			VLOG_WARN("Pod: %d Aggr Switches: %d(%d)(%d)\t",aggr_node[j]->pod, aggr_node[j]->fattree_index, aggr_node[j]->sw_indexTmp, aggr_node[j]->index);
			j++;
		}
	}	
	VLOG_WARN("Pod: %d Aggr Switches: %d(%d)(%d)\t",node->pod, node->fattree_index, node->sw_indexTmp, node->index);
}


void init_fattree_indices(void)
{
	int i;
	int core_index = MAX_EDGE+MAX_AGGR;
	DCSwitch_node* node;
	VLOG_WARN("Initializing fattree indices.");

	for(i=0;i<K;i++)
		last_edge[i]=0;
	
	for(i=0; i<MAX_SWITCHES; i++)
	{
		node = DCSwitches[i];
		if(node == NULL)
			continue;

		{
		if(node->level == CORE)
		{
			assign_core_indices(node, core_index);
			core_index++;
		}
		else if(node->level == AGGR)
			assign_aggr_indices(node);
		else if(node->level == EDGE);
			//assign_edge_indices();
		}
	}
	if(curr_pod!= K)
	{
		if(curr_pod < K)
			VLOG_WARN("Something's wrong: There are fewer than %d pods are there. Curr_pod: %d", K, curr_pod);
		else
			VLOG_WARN("Something's wrong: There are more than %d pods are there. Curr_pod: %d", K, curr_pod);
	}
	//print_connectivity();	
	for(i=0; i<=K; i++)
	{
		print_pod(i);
		printf("\n");
	}
	insert_downward_flow_entries();

}	


void print_pod(int pod)
{
	int i;
	DCSwitch_node *node;
	VLOG_WARN("Switches in Pod %d",pod);
	for(i=0; i< MAX_SWITCHES; i++)
	{	
		node = DCSwitches[i];
		if(node == NULL) continue;
		{
			if(node->pod == pod)
				VLOG_WARN("Switch %d fattree_index %d level %d dpid %llx", node->index, node->fattree_index, node->level, node->datapath_id);
		}
	}
}

void print_connectivity(void)
{
	int i,j;
	DCSwitch_node *node;
	FILE *fp = fopen("switchMap","w");
	for(i=0; i< MAX_SWITCHES; i++)
	{	
		node = DCSwitches[i];
		if(node == NULL) continue;
		for(j=0;j<K; j++)
		{
			if(node->neighbours[j]!=NULL)
			fprintf(stdout,"Switch %d fattree_index: %d atapath_id: %llx port %d ->> %d fattree_index: %d %llx\n", node->index , node->fattree_index, node->datapath_id, node->ports[j][0], node->neighbours[j]->index, node->neighbours[j]->fattree_index, node->neighbours[j]->datapath_id);
		}
		fprintf(stdout,"\n");
	}
	fclose(fp);
}

void insert_downward_flow_entries(void)
{
	int i;
	DCSwitch_node *node;
	for(i=0; i< MAX_SWITCHES; i++)
	{
		node = DCSwitches[i];
		if(node==NULL)	continue;
		{
			VLOG_DBG("Trying to insert flow entries in %d", node->sw_indexTmp);
			init_switch_prefixes(node->index, node->sw, node->rconn);	
		}
	}
	
}



